#include "log.h"
#include "fileParser.h"

using namespace std;

static char *av_fourcc_make_string(char *buf, uint32_t fourcc)
{
	int i;
	char *orig_buf = buf;
	size_t buf_size = AV_FOURCC_MAX_STRING_SIZE;

	for (i = 0; i < 4; i++)
	{
		const int c = fourcc & 0xff;
		const int print_chr = (c >= '0' && c <= '9') ||
							  (c >= 'a' && c <= 'z') ||
							  (c >= 'A' && c <= 'Z') ||
							  (c && strchr(". -_", c));
		const int len = snprintf(buf, buf_size, print_chr ? "%c" : "[%d]", c);
		if (len < 0)
			break;
		buf += len;
		buf_size = buf_size > len ? buf_size - len : 0;
		fourcc >>= 8;
	}

	return orig_buf;
}

/**** printf input format ****/
static void dump_format(AVFormatContext *input_context, int video_index, int audio_index)
{
	if (video_index != -1 && input_context->streams[video_index]->codec)
	{

		AVStream *st = input_context->streams[video_index];

		char videoInfo[4096] = "";
		snprintf(videoInfo, sizeof(videoInfo), "video:%s (%s)", avcodec_get_name(st->codec->codec_id), avcodec_profile_name(st->codec->codec_id, st->codec->profile));

		if (st->codec->codec_tag)
		{
			char codec_tag[128] = "";
			snprintf(videoInfo + strlen(videoInfo), sizeof(videoInfo), " (%s)", av_fourcc_make_string(codec_tag, st->codec->codec_tag));
		}
		else
		{

			snprintf(videoInfo + strlen(videoInfo), sizeof(videoInfo), " (%s)", "no codec tag");
		}

		char *pix_fomat = av_get_pix_fmt_name(st->codec->pix_fmt);
		snprintf(videoInfo + strlen(videoInfo), sizeof(videoInfo), " %s,", pix_fomat ? pix_fomat : "unkown");

		snprintf(videoInfo + strlen(videoInfo), sizeof(videoInfo), " %dx%d,", st->codec->width, st->codec->height);

		int fps = st->avg_frame_rate.den && st->avg_frame_rate.num;
		int tbr = st->r_frame_rate.den && st->r_frame_rate.num;
		int tbn = st->time_base.den && st->time_base.num;

		snprintf(videoInfo + strlen(videoInfo), sizeof(videoInfo), " fps:%0.2f, tbr:%0.2f, tbn:%0.2f", fps ? av_q2d(st->avg_frame_rate) : 0.0, tbr ? av_q2d(st->r_frame_rate) : 0.0, tbn ? 1 / av_q2d(st->time_base) : 0.0);
		LOG("%s\n", videoInfo);
	}

	if (audio_index != -1 && input_context->streams[audio_index]->codec)
	{

		AVStream *st = input_context->streams[audio_index];

		char audioInfo[4096] = "";
		snprintf(audioInfo, sizeof(audioInfo), "audio:%s (%s),", avcodec_get_name(st->codec->codec_id), avcodec_profile_name(st->codec->codec_id, st->codec->profile));

		snprintf(audioInfo + strlen(audioInfo), sizeof(audioInfo), " %d Hz", st->codec->sample_rate);

		LOG("%s\n", audioInfo);
	}
}

#define GETAVERROR(func, ret)                   \
	{                                           \
		char __tmp[1024];                       \
		av_strerror(ret, __tmp, sizeof(__tmp)); \
		LOG("%s failed:%s\n", func, __tmp);     \
	}

static int interrupt_cb(void *ctx)
{
	// TODO no handle
	return 0;
}

int FileParser::open_input(string inputfile)
{
	LOG("open input file:%s\n", inputfile.c_str());

	input_context = avformat_alloc_context();
	(input_context)->interrupt_callback.callback = interrupt_cb;

	int ret = avformat_open_input(&input_context, inputfile.c_str(), NULL, NULL);
	if (ret < 0)
	{
		GETAVERROR("avformat_open_input", ret);
		return ret;
	}

	ret = avformat_find_stream_info(input_context, nullptr);
	if (ret < 0)
	{
		GETAVERROR("avformat_find_stream_info", ret);
		return ret;
	}

	for (unsigned i = 0; i < (input_context)->nb_streams; i++)
	{
		if ((input_context)->streams[i]->codec->codec_type == AVMediaType::AVMEDIA_TYPE_VIDEO)
		{
			video_index = i;
			frame_rate = av_q2d(input_context->streams[i]->avg_frame_rate);
			if (frame_rate - (float)((int)frame_rate) > 0.00000)
				frame_rate = frame_rate + 1.0;
		}
		else if ((input_context)->streams[i]->codec->codec_type == AVMediaType::AVMEDIA_TYPE_AUDIO)
		{
			audio_index = i;
		}
	}

	if (video_index == -1 && audio_index == -1)
	{
		LOG("no any stream\n");
		return -1;
	}
	dump_format(input_context, video_index, audio_index);

	if ((string(input_context->iformat->name) == "h264" || string(input_context->iformat->name) == "hevc"))  // 裸数据没有pts，不能seek
		this->is_no_seek = true;

	if (audio_index != -1)
	{
		if (input_context->streams[audio_index]->codec->codec_id != AV_CODEC_ID_AAC || string(input_context->iformat->name) == "aac")
		{
			if (get_audio_coder() < 0) // 获取音频编解码器和滤镜
				return -1;
		}
		else
		{
			decode_context = input_context->streams[audio_index]->codec;
		}

		sample_rate = input_context->streams[audio_index]->codec->sample_rate;
	}

	if (video_index != -1)
	{
		if (input_context->streams[video_index]->codec->codec_id == AV_CODEC_ID_H264)
		{
			is_h264 = true;
			video_abs_filter = av_bsf_get_by_name("h264_mp4toannexb");
		}
		else if (input_context->streams[video_index]->codec->codec_id == AV_CODEC_ID_H265)
		{
			is_h264 = false;
			video_abs_filter = av_bsf_get_by_name("hevc_mp4toannexb");
		}
		else
		{
			LOG("not 264 or 265, exit\n");
			return -1;
		}

		if ((ret = av_bsf_alloc(video_abs_filter, &video_abs_ctx)) != 0) // 创建视频bitstream
		{
			GETAVERROR("av_bsf_alloc", ret);
			return -1;
		}
		avcodec_parameters_copy(video_abs_ctx->par_in, input_context->streams[video_index]->codecpar);

		if ((ret = av_bsf_init(video_abs_ctx)) != 0) // 初始化bitstream
		{
			GETAVERROR("av_bsf_init", ret);
			return -1;
		}
	}

	return ret;
}

AVPacket *read_packet(AVFormatContext *input_context)
{
	AVPacket *packet = new AVPacket();
	av_init_packet(packet);
	int ret = av_read_frame(input_context, packet);
	if (ret >= 0)
	{
		return packet;
	}
	else
	{
		av_packet_unref(packet);
		delete packet;
		return nullptr;
	}
}

int init_audio_encoder(AVStream *inputStream, AVCodecContext **encode_context)
{
	int ret = 0;
	AVCodec *audioCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
	if (!audioCodec)
	{

		LOG("avcodec_find_encoder failed");
		return -1;
	}
	*encode_context = avcodec_alloc_context3(audioCodec);
	(*encode_context)->codec = audioCodec;
	(*encode_context)->sample_rate = inputStream->codecpar->sample_rate;
	(*encode_context)->channel_layout = inputStream->codecpar->channel_layout;
	(*encode_context)->channels = inputStream->codecpar->channels;
	if ((*encode_context)->channel_layout == 0)
	{
		(*encode_context)->channel_layout = av_get_default_channel_layout(inputStream->codecpar->channels);
	}
	(*encode_context)->sample_fmt = audioCodec->sample_fmts[0];

	(*encode_context)->codec_tag = 0;
	ret = avcodec_open2((*encode_context), audioCodec, 0);
	return ret;
}

int init_audio_decoder(AVFormatContext *input_context, AVCodecContext **decode_context, int audio_index)
{
	if (audio_index == -1)
		return 1;
	AVCodec *pdec;
	int ret = av_find_best_stream(input_context, AVMEDIA_TYPE_AUDIO, -1, -1, &pdec, 0);
	if (ret < 0)
	{
		GETAVERROR("av_find_best_stream", ret);
		return ret;
	}
	*decode_context = avcodec_alloc_context3(pdec);
	avcodec_parameters_to_context(*decode_context, input_context->streams[audio_index]->codecpar);
	if (input_context->streams[audio_index]->codec->codec_id == AV_CODEC_ID_PCM_ALAW || input_context->streams[audio_index]->codec->codec_id == AV_CODEC_ID_PCM_MULAW)
	{
		(*decode_context)->sample_rate = 8000;
	}
	else
	{
		(*decode_context)->sample_rate = input_context->streams[audio_index]->codec->sample_rate;
	}
	(*decode_context)->time_base = input_context->streams[audio_index]->codec->time_base;
	ret = avcodec_open2(*decode_context, pdec, NULL);
	return 0;
}

int init_audio_filter(AVFilterGraph **audio_graph, AVCodecContext *decode_context, AVFilterContext **bf_src_ctx_audio, AVFilterContext **bf_sink_ctx_audio, AVCodecContext *encode_audio)
{
	char args[512];
	int ret = 0;
	const AVFilter *abuffersrc = avfilter_get_by_name("abuffer");
	const AVFilter *abuffersink = avfilter_get_by_name("abuffersink");
	AVFilterInOut *outputs = avfilter_inout_alloc();
	AVFilterInOut *inputs = avfilter_inout_alloc();
	static const enum AVSampleFormat out_sample_fmts[] = {encode_audio->sample_fmt, AV_SAMPLE_FMT_NONE};
	static const int64_t out_channel_layouts[] = {encode_audio->channel_layout, -1};
	static const int out_sample_rates[] = {encode_audio->sample_rate, -1};
	const AVFilterLink *outlink;
	AVRational time_base = decode_context->time_base;

	*audio_graph = avfilter_graph_alloc();
	if (!outputs || !inputs || !(*audio_graph))
	{
		ret = AVERROR(ENOMEM);
		goto end;
	}

	/* buffer audio source: the decoded frames from the decoder will be inserted here. */
	if (!decode_context->channel_layout)
		decode_context->channel_layout = av_get_default_channel_layout(decode_context->channels);
	snprintf(args, sizeof(args),
			 "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%d",
			 time_base.num, time_base.den, decode_context->sample_rate,
			 av_get_sample_fmt_name(decode_context->sample_fmt), decode_context->channel_layout);
	ret = avfilter_graph_create_filter(bf_src_ctx_audio, abuffersrc, "in",
									   args, NULL, *audio_graph);
	if (ret < 0)
	{
		GETAVERROR("avfilter_graph_create_filter", ret);
		goto end;
	}

	/* buffer audio sink: to terminate the filter chain. */
	ret = avfilter_graph_create_filter(bf_sink_ctx_audio, abuffersink, "out",
									   NULL, NULL, *audio_graph);
	if (ret < 0)
	{
		GETAVERROR("avfilter_graph_create_filter", ret);
		goto end;
	}

	ret = av_opt_set_int_list(*bf_sink_ctx_audio, "sample_fmts", out_sample_fmts, -1,
							  AV_OPT_SEARCH_CHILDREN);
	if (ret < 0)
	{
		GETAVERROR("av_opt_set_int_list1", ret);
		goto end;
	}

	ret = av_opt_set_int_list(*bf_sink_ctx_audio, "channel_layouts", out_channel_layouts, -1,
							  AV_OPT_SEARCH_CHILDREN);
	if (ret < 0)
	{
		GETAVERROR("av_opt_set_int_list2", ret);
		goto end;
	}

	ret = av_opt_set_int_list(*bf_sink_ctx_audio, "sample_rates", out_sample_rates, -1,
							  AV_OPT_SEARCH_CHILDREN);
	if (ret < 0)
	{
		GETAVERROR("av_opt_set_int_list3", ret);
		goto end;
	}

	outputs->name = av_strdup("in");
	outputs->filter_ctx = *bf_src_ctx_audio;
	outputs->pad_idx = 0;
	outputs->next = NULL;

	inputs->name = av_strdup("out");
	inputs->filter_ctx = *bf_sink_ctx_audio;
	inputs->pad_idx = 0;
	inputs->next = NULL;

	if ((ret = avfilter_graph_parse_ptr(*audio_graph, "anull",
										&inputs, &outputs, NULL)) < 0)
		goto end;

	if ((ret = avfilter_graph_config(*audio_graph, NULL)) < 0)
		goto end;

	// av_buffersink_set_frame_size(*bf_sink_ctx_audio, 1024);
	av_buffersink_set_frame_size(*bf_sink_ctx_audio, encode_audio->frame_size);
end:
	avfilter_inout_free(&inputs);
	avfilter_inout_free(&outputs);
	return ret;
}

AVFrame *decode_audio(AVPacket *packet, AVCodecContext *decode_context)
{
	int ret = avcodec_send_packet(decode_context, packet);
	av_packet_free(&packet);
	delete packet;

	if (ret < 0)
	{
		return nullptr;
	}

	AVFrame *frame = av_frame_alloc();
	ret = avcodec_receive_frame(decode_context, frame);
	if (ret >= 0)
	{
		return frame;
	}
	else
	{
		av_frame_free(&frame);
		return nullptr;
	}
}

AVPacket *encode_audio(AVFrame *frame, AVCodecContext *encode_context)
{
	int ret = 0;

	ret = avcodec_send_frame(encode_context, frame);
	if (ret < 0)
		return nullptr;

	AVPacket *packet = new AVPacket;
	av_init_packet(packet);
	ret = avcodec_receive_packet(encode_context, packet);
	if (ret >= 0)
		return packet;
	av_packet_unref(packet);
	delete packet;
	return nullptr;
}

int FileParser::filter_frame(AVFrame *frame, int flag, list<AVPacket *>* packet_vecs)
{
	int ret = av_buffersrc_add_frame_flags(bf_src_ctx_audio, frame, AV_BUFFERSRC_FLAG_PUSH);
	if (ret < 0)
	{
		av_frame_free(&frame);
		return -1;
	}

	while (1)
	{
		AVFrame *filterFrame = av_frame_alloc();
		ret = av_buffersink_get_frame(bf_sink_ctx_audio, filterFrame);
		if ((ret == AVERROR(EAGAIN) || ret == AVERROR_EOF))
		{
			av_frame_free(&frame);
			av_frame_free(&filterFrame);
			return 0;
		}
		else if (ret < 0)
		{
			av_frame_free(&frame);
			av_frame_free(&filterFrame);
			return -1;
		}

		if (string(input_context->iformat->name) == "aac")
		{ // 兼容aac的frame_size != 1024
			filterFrame->pts = filterFrame->best_effort_timestamp;
		}

		AVPacket *packet1 = encode_audio(filterFrame, encode_context); // 编码
		if (packet1)
		{
			packet1->stream_index = audio_index;
			if (flag == 1)
			{
				if (first_audio_extra == NULL)
				{
					first_audio_extra_len = encode_context->extradata_size;
					first_audio_extra = new unsigned char[first_audio_extra_len];
					memcpy(first_audio_extra, encode_context->extradata, first_audio_extra_len); // aac header
				}
				av_frame_free(&frame);
				av_frame_free(&filterFrame);
				return 0;
			}
			else if (flag == 0)
			{
				packet_vecs->push_back(packet1);
			}
		}
		av_frame_free(&filterFrame);
	}
}

int FileParser::get_audio_coder()
{
	int ret = init_audio_decoder(input_context, &decode_context, audio_index);
	if (ret != 0)
		return -1;

	ret = init_audio_encoder(input_context->streams[audio_index], &encode_context);
	if (ret < 0)
	{
		return -1;
	}
	ret = init_audio_filter(&audio_graph, decode_context, &bf_src_ctx_audio, &bf_sink_ctx_audio, encode_context);
	if (ret < 0)
	{
		return -1;
	}

	return 0;
}

int FileParser::get_extra_data(string filename)
{
	if (open_input("./records/" + filename) < 0)
		return -1;

	if (video_index == -1)
		first_video_extra = (unsigned char *)100; // unuse

	if (audio_index == -1)
		first_audio_extra = (unsigned char *)100; // unuse

	return get_avpacket(filename, nullptr, 2, 1);
}

int adts2asc(AVBSFContext *&aac_bsf_ctx, AVBitStreamFilter *&aac_bsf_filter, AVPacket *&packet)
{
	if (aac_bsf_ctx == NULL)
	{
		aac_bsf_filter = (AVBitStreamFilter *)av_bsf_get_by_name("aac_adtstoasc");
		av_bsf_alloc(aac_bsf_filter, &aac_bsf_ctx);
	}
	if (av_bsf_send_packet(aac_bsf_ctx, packet) >= 0)
	{
		if (av_bsf_receive_packet(aac_bsf_ctx, packet) < 0)
		{
			return -1;
		}
	}
	return 0;
}

/*data_type:0, read video only
		   :1, read audio only
		   :2, read video and audio
  flag:0 读取音视频数据
  flag:1 读取音视频配置信息
*/
int FileParser::get_avpacket(string inputfile, list<AVPacket *>* packet_vecs, int data_type, int flag)
{

	if (input_context == NULL)
	{
		if (open_input("./records/" + inputfile) < 0)
			return -1;
	}
	AVPacket *packet = NULL;
	int ret = -1;
	while (1)
	{
		if (flag == 1)
		{
			if (first_video_extra != NULL && first_audio_extra != NULL) // 配置信息都读取完成
				return 0;
		}

		packet = read_packet(input_context);
		if (packet == NULL)
		{
			LOG("no data\n");
			ret == -1;
			break;
		}

		if (packet->stream_index != audio_index && packet->stream_index != video_index)
		{
			av_packet_free(&packet);
			continue;
		}

		if (packet->stream_index == audio_index && data_type != 0)
		{
			packet->stream_index = audio_index;

			// aac裸文件也重新编码，因为我不会计算adts格式的aac config -_-！
			if (decode_context->codec_id == AV_CODEC_ID_AAC && string(input_context->iformat->name) != "aac")
			{
				if (decode_context->extradata_size == 0 && adts2asc(aac_bsf_ctx, aac_bsf_filter, packet)) // adts format
				{
					LOG("adts2asc failed\n");
					av_packet_free(&packet);
					return -1;
				}

				if (flag == 1) // flag ==1,只读取配置信息
				{
					if (first_audio_extra == NULL && decode_context->extradata_size != 0)
					{

						first_audio_extra_len = decode_context->extradata_size;
						first_audio_extra = new unsigned char[first_audio_extra_len];
						memcpy(first_audio_extra, decode_context->extradata, first_audio_extra_len);
					}
					av_packet_free(&packet);
					continue;
				}
				if (flag == 0)
				{
					packet_vecs->push_back(packet);
					return 0;
				}
			}
			else // not aac, convert to aac
			{
				AVFrame *frame = decode_audio(packet, decode_context); // 解码
				if (frame == NULL)
				{
					continue;
				}
				frame->pts = frame->best_effort_timestamp;
				filter_frame(frame, flag, packet_vecs); // 滤镜
				if (flag == 0)
					return 0;
			}
		}
		else if (packet->stream_index == video_index && data_type != 1)
		{
			av_bsf_send_packet(video_abs_ctx, packet);
			ret = av_bsf_receive_packet(video_abs_ctx, packet);

			if (flag == 1) // flag ==1,只读取配置信息
			{
				if (first_video_extra == NULL)
				{
					if (input_context->streams[video_index]->codec->extradata_size > 4)
					{
						unsigned char *extradata = input_context->streams[video_index]->codec->extradata;
						// 原始raw data 是 annexb，带startcode的sps pps vps在extradata中
						if (extradata[0] == 0x00 && extradata[1] == 0x00 && (extradata[2] == 0x00 || extradata[2] == 0x01))
						{
							first_video_extra_len = input_context->streams[video_index]->codec->extradata_size + 3;
							first_video_extra = new unsigned char[first_video_extra_len];
							memcpy(first_video_extra, input_context->streams[video_index]->codec->extradata, first_video_extra_len - 3);
							first_video_extra[first_video_extra_len - 3] = 0x00;
							first_video_extra[first_video_extra_len - 3 + 1] = 0x00;
							first_video_extra[first_video_extra_len - 3 + 2] = 0x01; // 加分隔符
						}
						else if(packet->size > 4 && packet->data[0] == 0x00 && packet->data[1] == 0x00 && (packet->data[2] == 0x00 || packet->data[2] == 0x01))
						{
							unsigned char byte = 0;
							if(packet->data[2] == 0x00 )
								byte = packet->data[4] & 0x1f;
							else
								byte = packet->data[3] & 0x1f;

							if(byte == 0x06 || byte == 0x07){  // SEI or sps
								first_video_extra_len = packet->size;
								first_video_extra = new unsigned char[first_video_extra_len];
								memcpy(first_video_extra, packet->data, first_video_extra_len);
							}else 
								return -2;
						}
					}
				}

				av_packet_free(&packet);
			}
			else if (flag == 0)
			{
				// h264 h265裸文件没有pts，需要自己生成
				if ((string(input_context->iformat->name) == "h264" || string(input_context->iformat->name) == "hevc"))
				{
					if (pts == 0)
					{
						pts = ((float)1 / frame_rate) / av_q2d(input_context->streams[video_index]->time_base);
					}
					last_pts += pts;
					packet->pts = packet->dts = last_pts;
				}

				packet_vecs->push_back(packet);
				return 0;
			}
		}
		else
		{
			av_packet_free(&packet);
			continue;
		}
	}

	if (packet)
	{
		av_packet_free(&packet);
	}
	return -1;
}

int FileParser::av_release()
{
	if (encode_context)
	{
		avcodec_close(encode_context);
		avcodec_free_context(&encode_context);
	}
	if (decode_context && decode_context != input_context->streams[audio_index]->codec)
	{ // 如果音频是aac，decode_context就是输入上下文的codec。防止double free
		avcodec_close(decode_context);
		avcodec_free_context(&decode_context);
	}

	if (audio_graph)
		avfilter_graph_free(&audio_graph);
	if (input_context)
	{
		avformat_close_input(&input_context);
		avformat_free_context(input_context);
	}
	if (video_abs_ctx)
		av_bsf_free(&video_abs_ctx);

	if (aac_bsf_ctx)
		av_bsf_free(&aac_bsf_ctx);
}

int FileParser::do_av_seek(float start_time)
{
	if (is_no_seek) 
		return -1;

	int64_t seek_target = start_time * AV_TIME_BASE;
	if (av_seek_frame(input_context, -1, seek_target, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME) < 0)
	{
		return -1;
	}

	return 0;
}